package su.theravada.jpalireader;

import java.awt.Cursor;
import java.awt.Frame;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.InputStream;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.EventObject;
import java.util.Locale;
import java.util.ResourceBundle;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPopupMenu;
import javax.swing.JToolBar;

import org.lobobrowser.html.gui.HtmlBlockPanel;
import org.lobobrowser.html.gui.HtmlPanel;
import org.lobobrowser.html.gui.LayoutCompleteListener;
import org.lobobrowser.html.test.SimpleHtmlRendererContext;
import org.lobobrowser.html.test.SimpleUserAgentContext;

import su.theravada.jpalireader.entities.AppState;
import su.theravada.jpalireader.entities.NodeInfo;
import su.theravada.jpalireader.openBookDlg.OpenBookDlg;
import su.theravada.jpalireader.util.SqlUtil;
import su.theravada.jpalireader.util.UTF8Control;
import su.theravada.jpalireader.util.ZipUtil;

public class BookFrame extends HtmlPanel
{
	private HtmlBlockPanel _blockPanel;
	private String _BookName;
	private int _NodeID;
	private String _BookmarkName;
	private String _PTSPage;
	private int _ScrollPos;
	private OpenBookDlg _openBookDlg;
	private int _BookNodeID;
	private SearchBookDlg _searchBookDlg;
	private String _bookFileName;
	private int _searchMatchCount;
	private int _currentSearchMatch;
	private boolean _showBold=true;
	private boolean _showFont=true;
	
	public BookFrame(int NodeID,int ScrollPos)
	{
		super();	
		_NodeID=NodeID;
		_ScrollPos=ScrollPos;
		_BookmarkName="";
		_PTSPage="";		
		
		LoadBook();
	}
	
	
	public BookFrame(int NodeID,String strPTSPage)
	{
		super();
		_NodeID=NodeID;
		_PTSPage=strPTSPage;
		_ScrollPos=-1;		
		
		LoadBook();		
	}
	
	private void LoadBook()
	{		
        ResultSet rs = null;
        PreparedStatement psSelect = null;
        PreparedStatement psParent = null;
        ResultSet rsParent=null;
        
		try
		{			
            psSelect=MainWindow.getDBConnection().prepareStatement("SELECT files.FileName,nodes.nodetitle,files.FileID,nodes.BookmarkName,files.showbold,files.showfont FROM files INNER JOIN nodes ON files.FileID=nodes.FileID WHERE NodeID=?");
            psSelect.setInt(1, _NodeID);
            
            rs = psSelect.executeQuery();
            if(rs.next())
            {
            	_bookFileName=rs.getString("FileName");
            	_BookmarkName=rs.getString("BookmarkName")==null ? "" : rs.getString("BookmarkName");
            	_showBold=rs.getBoolean("ShowBold");
            	_showFont=rs.getBoolean("ShowFont");
            	            	
            	psParent=MainWindow.getDBConnection().prepareStatement("SELECT nodeid,nodetitle FROM nodes WHERE FileID=? ORDER BY ParentID FETCH FIRST ROW ONLY");
            	psParent.setInt(1, rs.getInt("FileID"));
            	rsParent=psParent.executeQuery();
            	if(rsParent.next())
            	{
            		_BookName=rsParent.getString("nodetitle");
            		_BookNodeID=rsParent.getInt("nodeid");
            	}
            }                         
		}
		catch(SQLException sqle)
		{		
			SqlUtil.ReportSQLException(sqle);
		}
		catch(Exception ex)
		{		
			ex.printStackTrace();					
		}
		finally
		{
			SqlUtil.ClosePreparedStatement(psSelect);
			SqlUtil.CloseResultSet(rs);
			SqlUtil.ClosePreparedStatement(psParent);
			SqlUtil.CloseResultSet(rsParent);			
		}
		
		_openBookDlg=new OpenBookDlg(((MainWindow)getTopLevelAncestor()),_BookNodeID);
		_searchBookDlg=new SearchBookDlg(((MainWindow)getTopLevelAncestor()));
		_searchBookDlg.setLocationRelativeTo(null);
		
		String strFileContent=ZipUtil.LoadContentFile(_bookFileName,Paths.CanonPath,_showBold,_showFont);								

	    SimpleHtmlRendererContext rcontext=new SimpleHtmlRendererContext(this, new SimpleUserAgentContext());	
	    setHtml(strFileContent, "file:///"+System.getProperty("user.dir")+"/", rcontext);	    	    	   
	    	    
	    _blockPanel=getHtmlBlockPanel();	
	   
	    if(_blockPanel!=null)
	    {	    	
			ResourceBundle res_strings = ResourceBundle.getBundle("strings", Locale.getDefault(),new UTF8Control());
	    	JPopupMenu objPopupMenu=new JPopupMenu();	    
		    JMenuItem ctlCopy=new JMenuItem(res_strings.getString("Copy"));
		    JMenuItem ctlLookup= new JMenuItem(res_strings.getString("Lookup"));
		    JMenuItem ctlGoTo= new JMenuItem(res_strings.getString("GoTo"));
		    JMenuItem ctlBookmark= new JMenuItem(res_strings.getString("Bookmark"));
		    JMenuItem ctlSearchBook= new JMenuItem(res_strings.getString("SearchBook"));
		    JMenuItem ctlBookFormatting= new JMenuItem(res_strings.getString("BookFormatting"));
		    
		    objPopupMenu.add(ctlLookup);
		    objPopupMenu.add(ctlCopy);
		    objPopupMenu.add(ctlGoTo);
		    objPopupMenu.add(ctlBookmark);
		    objPopupMenu.add(ctlSearchBook);
		    objPopupMenu.add(ctlBookFormatting);
		    
	        ctlCopy.addActionListener(new ActionListener() {
	        	public void actionPerformed(ActionEvent event)
	        	{
	        		_blockPanel.copy();
	        	}	        	
	        });
		    
	        ctlLookup.addActionListener(new ActionListener(){
	        	public void actionPerformed(ActionEvent event)
	        	{
	        		((MainWindow)getTopLevelAncestor()).DictionaryLookup(getSelectionText());
	        	}	  
	        });
	        
	        ctlGoTo.addActionListener(new ActionListener() {
	        	public void actionPerformed(ActionEvent event)
	        	{
	        		GoToActionPerformed(event);
	        	}
	        });
	        
	        ctlBookmark.addActionListener(new ActionListener() {
	        	public void actionPerformed(ActionEvent event)
	        	{
	        		BookmarkActionPerformed(event);
	        	}
	        });
	        
	        ctlSearchBook.addActionListener(new ActionListener() {
	        	public void actionPerformed(ActionEvent event)
	        	{
	        		SearchBookActionPerformed(event);
	        	}
	        });
	        
	        ctlBookFormatting.addActionListener(new ActionListener() {
	        	public void actionPerformed(ActionEvent event)
	        	{
	        		BookFormattingActionPerformed(event);
	        	}
	        });
	        	        
	    	_blockPanel.setComponentPopupMenu(objPopupMenu);
	    	
	    	_blockPanel.addLayoutCompleteEventListener(new LayoutCompleteListener()
	    	{
	    		public void LayoutComplete(EventObject event)
	    		{
	    			LayoutCompleteEventFired(event);
	    		}
	    	}); 
	    }		
	    
	    _searchBookDlg.ctlClose.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) 
			{
				_searchBookDlg.setVisible(false);				
			}	    	
	    });
	    
	    _searchBookDlg.ctlFind.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				FindActionPerformed(e);				
			}	    	
	    });
	    
	    //search functions
	    _searchBookDlg.ctlFirstMatch.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent arg0) {
				scrollToElement("sb1");
				UpdateScrollBar();
	    		_currentSearchMatch=1;
			}	    	
	    });
	    
	    _searchBookDlg.ctlLastMatch.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent arg0) {
				scrollToElement("sb"+_searchMatchCount);
				UpdateScrollBar();
	    		_currentSearchMatch=_searchMatchCount;
			}	    	
	    });
	    
	    _searchBookDlg.ctlPrevMatch.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent arg0) {
				if(_currentSearchMatch!=1)
				{
					_currentSearchMatch=_currentSearchMatch==0 ? 1 : _currentSearchMatch-1;
										
					scrollToElement("sb"+_currentSearchMatch);
					UpdateScrollBar();
				}
			}	    	
	    });
	    
	    _searchBookDlg.ctlNextMatch.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent arg0) {
				if(_currentSearchMatch!=_searchMatchCount)
				{
					_currentSearchMatch=_currentSearchMatch==0 ? 1 : _currentSearchMatch+1;
										
					scrollToElement("sb"+_currentSearchMatch);
					UpdateScrollBar();
				}
			}	    	
	    });
	}
	
	private void FindActionPerformed(ActionEvent e)
	{
		try
		{
			setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));	
			
			String strFileContent=ZipUtil.LoadContentFile(_bookFileName,Paths.CanonPath,_showBold,_showFont);;
			
			//perform search in body only to avoid document corruption
			Pattern objBodyPattern=Pattern.compile("(.*<body>)(.*)(</body>.*)",Pattern.DOTALL | Pattern.CASE_INSENSITIVE);
			Matcher objBodyMatcher=objBodyPattern.matcher(strFileContent);
	
			if(objBodyMatcher.find())
			{
				String strBody=objBodyMatcher.group(2);
				
				Matcher objReplaceMatcher=Pattern.compile(_searchBookDlg.ctlSearchText.getText(),Pattern.CASE_INSENSITIVE).matcher(strBody);
				StringBuffer sb = new StringBuffer();
				_searchMatchCount=0;
				while (objReplaceMatcher.find()) 
				{
					_searchMatchCount++;
					objReplaceMatcher.appendReplacement(sb, "<a name='sb"+_searchMatchCount+
							"'><span style='background-color:yellow'>$0</span></a>");
				}
				objReplaceMatcher.appendTail(sb);
				
				strFileContent=objBodyMatcher.group(1)+sb.toString()+objBodyMatcher.group(3);
				
				_searchBookDlg.ctlNavButtons.setVisible(_searchMatchCount!=0);			
			}
			
		    SimpleHtmlRendererContext rcontext=new SimpleHtmlRendererContext(this, new SimpleUserAgentContext());	
		    setHtml(strFileContent, "file:///"+System.getProperty("user.dir")+"/", rcontext);
		}
		finally
		{
			setCursor(Cursor.getDefaultCursor());
		}
	}
	
	private void LayoutCompleteEventFired(EventObject event)
	{
		if(_BookmarkName!=null && !_BookmarkName.isEmpty())
		{
			scrollToElement("p"+_BookmarkName);
			UpdateScrollBar();
		}
		
		if(_PTSPage!=null && !_PTSPage.isEmpty())
		{
			scrollToElement("g"+_PTSPage);
			UpdateScrollBar();
		}
		
		if(_ScrollPos>0)
		{
			scroll(0, _ScrollPos);
			UpdateScrollBar();
		}
	}
	
	private void UpdateScrollBar()
	{
		//for some unknown bug in cobra toolkit the scroll bar is not updated after scroll and scrollToElement
		scrollBy(0, 1);
		scrollBy(0, -1);
	}
	
	
	private void GoToActionPerformed(ActionEvent event)
	{
		_openBookDlg.setSize(550,450);
		_openBookDlg.setLocationRelativeTo(null);
		_openBookDlg.setVisible(true);
		
		if(_openBookDlg.isDialogSuccess())
		{		
			if(!_openBookDlg.getPTSPage().isEmpty())
			{
				scrollToElement("g"+_openBookDlg.getPTSPage());
				UpdateScrollBar();
			}
			else
			{
				ResultSet rs = null;
		        PreparedStatement psSelect = null;
		        
				try
				{			
		            psSelect=MainWindow.getDBConnection().prepareStatement("SELECT BookmarkName FROM nodes WHERE NodeID=?");
		            psSelect.setInt(1, _openBookDlg.getNodeID());		            
		            rs = psSelect.executeQuery();
		            if(rs.next())
		            {
		            	String strBookmarkName=rs.getString("BookmarkName")==null ? "" : rs.getString("BookmarkName");
		            	
		            	if(!strBookmarkName.isEmpty())
		            	{
		            		scrollToElement("p"+strBookmarkName);
			    			scrollBy(0, 1);//this will repaint the window and update the scrollbars
		            	}
		            }
				}
				catch(SQLException sqle)
				{		
					SqlUtil.ReportSQLException(sqle);
				}
				catch(Exception ex)
				{		
					ex.printStackTrace();					
				}
				finally
				{
					SqlUtil.ClosePreparedStatement(psSelect);
					SqlUtil.CloseResultSet(rs);					
				}
			}
		}
	}	

	private void BookmarkActionPerformed(ActionEvent event)
	{
		ResourceBundle res_strings = ResourceBundle.getBundle("strings", Locale.getDefault(),new UTF8Control());
		
		String strBookMarkName = (String)JOptionPane.showInputDialog(
				((MainWindow)getTopLevelAncestor()),
				res_strings.getString("EnterBookmarkName"),"",
                JOptionPane.PLAIN_MESSAGE,null,null,getBookName()+" "+getScrollPos());
		
		if(strBookMarkName!=null && !strBookMarkName.isEmpty())
		{
			AppState objAppState=((MainWindow)getTopLevelAncestor()).appState;
			AppState.BookmarkInfo objBookmark=objAppState.new BookmarkInfo(getNodeID(), getScrollPos(), strBookMarkName);
			objAppState.arrBookmarks.add(objBookmark);			
		}
	}
	
	private void SearchBookActionPerformed(ActionEvent event)
	{
		_searchBookDlg.setVisible(true);
	}
	
	private void BookFormattingActionPerformed(ActionEvent event)
	{
		BookFormattingDlg objDlg=new BookFormattingDlg(((MainWindow)getTopLevelAncestor()),_BookNodeID);
		objDlg.setSize(332,129);
		objDlg.setLocationRelativeTo(null);
		objDlg.setVisible(true);
		
		if(objDlg.DialogSuccess)
		{
			setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));	
			
			try
			{
				setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));	
				
				_showBold=objDlg.ctlShowBold.isSelected();
				_showFont=objDlg.ctlShowFont.isSelected();
				String strFileContent=ZipUtil.LoadContentFile(_bookFileName,Paths.CanonPath,_showBold,_showFont);;
				
			    SimpleHtmlRendererContext rcontext=new SimpleHtmlRendererContext(this, new SimpleUserAgentContext());	
			    setHtml(strFileContent, "file:///"+System.getProperty("user.dir")+"/", rcontext);
			}
			finally
			{
				setCursor(Cursor.getDefaultCursor());
			}
		}
	}
	
	private void BookLocationActionPerformed(ActionEvent event)
	{
		NodeInfo objNode=new NodeInfo(_BookNodeID,"",0);
		JOptionPane.showMessageDialog(this, objNode.getPath());
	}
	
	public String getBookName() {
		return _BookName;
	}

	public int getNodeID() {
		return _NodeID;
	}
	
	public int getScrollPos()
	{
		return _blockPanel.getScrollPos();
	}
	
	public int getBookNodeID() {
		return _BookNodeID;
	}

	public String getBookPath() {
		NodeInfo objNode=new NodeInfo(_BookNodeID,"",0);
		return objNode.getPath();
	}
}
